package com.yvan.leto;

import com.google.common.base.Splitter;
import com.yvan.IpUtils;
import com.yvan.platform.JsonWapper;
import com.yvan.platform.StringUtils;
import lombok.extern.slf4j.Slf4j;
import lombok.val;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.recipes.cache.*;

import java.util.List;

/**
 * leto 配置注册中心
 */
@Slf4j
public class ConfigRegistry {

    private static final String FMT_PROJECT_INSTANCE = "/${env}/instances/${project}/n";
    private static final String FMT_INSTANCE = "/${env}/projects/${project}/${segment}/instances/n";
    private static final String FMT_LS_SEGMENT = "/${env}/projects/${project}";
    private static final String FMT_SEGMENT_CONFIG = "/${env}/projects/${project}/${segment}/config";
    private static final String FMT_PROJECT = "/${env}/projects/${project}";
    private static final String FMT_PROJECT_WATCH = FMT_PROJECT;

    private static final String FMT_SERVER = "/servers/n";
    private static RegistryHelper registry;

    public void init(ZkProperties zkProperties) {
        registry = new RegistryHelperZk(zkProperties);
        registry.init();
    }

    public void updateProjectVersion(String env, String projectName, String projectVersion) {
        final String key = StringUtils.format(FMT_PROJECT)
                .set("env", env)
                .set("project", projectName)
                .build();
        registry.persist(key, projectVersion);
    }

    /*
    public void updateSegmentVersion(String env, String projectName, String segment, String segmentVersion) {
        final String key = StringUtils.format(FMT_SEGMENT_CONFIG)
                .set("env", env)
                .set("project", projectName)
                .set("segment", segment)
                .build();
        registry.persist(key, segmentVersion);
    }
    */

    public void registryServer() {
        //注册服务器
        final String data = new JsonWapper()
                .set("ip", IpUtils.getIp())
                .set("processId", IpUtils.getProcessId())
                .toString(false);
        registry.persistEphemeralSequential(FMT_SERVER, data);
    }

    List<String> loadSegments(String env, String project) {
        final String key = StringUtils.format(FMT_LS_SEGMENT)
                .set("env", LetoManage.appConfig.env)
                .set("project", project)
                .build();
        return registry.getChildrenKeys(key);
    }

    void writeInstance(String env, String project, List<String> segments) {
        String instanceData = new JsonWapper()
                .set("processId", IpUtils.getProcessId())
                .set("clientId", LetoManage.appConfig.clientId)
                .set("ip", IpUtils.getIp())
                .set("revId", RevUtils.getRevId(env,project))
                .toString();

        final String instanceKey = StringUtils.format(FMT_PROJECT_INSTANCE)
                .set("env", env)
                .set("project", project)
                .build();
        registry.persistEphemeralSequential(instanceKey, instanceData);

        for (String segment : segments) {
            final String key = StringUtils.format(FMT_INSTANCE)
                    .set("env", env)
                    .set("project", project)
                    .set("segment", segment)
                    .build();
            registry.persistEphemeralSequential(key, instanceData);
        }
    }


    boolean checkSegmentNodeExists(String env, String project, List<String> segments) {
        for (String segment : segments) {
            final String key = StringUtils.format(FMT_SEGMENT_CONFIG)
                    .set("env", env)
                    .set("project", project)
                    .set("segment", segment)
                    .build();
            if (!registry.isExisted(key)) {
                log.error(key + " not exists, app interrupted!");
                return false;
            }
        }
        return true;
    }

    boolean checkProjectNodeExists(String env, String project) {
        final String key = StringUtils.format(FMT_PROJECT)
                .set("env", env)
                .set("project", project)
                .build();
        if (!registry.isExisted(key)) {
            log.error(key + " not exists, app interrupted!");
            return false;
        }
        return true;
    }

    void watchProjectChange(final String env, final String project, final WatchProcess watchProcess) {
        final String listenerKey = StringUtils.format(FMT_PROJECT_WATCH)
                .set("env", env)
                .set("project", project)
                .build();
        final CuratorFramework client = (CuratorFramework) registry.getRawClient();
        final WatchProcessEvent arg = new WatchProcessEvent();
        arg.env = env;
        arg.project = project;

        final NodeCache projectEnvCache = new NodeCache(client,listenerKey);
        projectEnvCache.getListenable().addListener(new NodeCacheListener() {
            @Override
            public void nodeChanged() throws Exception {
                if (!registry.isExisted(listenerKey)) {
                    log.error(listenerKey + " not exists, app interrupted!");
                    watchProcess.interrupt(arg);
                }

                List<String> segmentChildList = client.getChildren().forPath(listenerKey);
                if(segmentChildList ==null || segmentChildList.isEmpty())
                    return;

                for(String path:segmentChildList){
                    String childFullPath = listenerKey + "/" + path;
                    List<String> sps = Splitter.on("/").omitEmptyStrings().trimResults().splitToList(childFullPath);
//                    if (sps.size() != 5 || !"config".equals(sps.get(sps.size() - 1)))
//                        continue;
                    val env = sps.get(0);
                    val project = sps.get(2);
                    val segment = SegmentHelper.getSegmentName(sps.get(3));
                    arg.project = project;
                    arg.env = env;
                    arg.segment = segment;
                    arg.segmentFullName = sps.get(3);
                    watchProcess.onChange(arg);
                }
            }
        });
        //开始监听
        try {
            projectEnvCache.start();
        } catch (Exception e) {
            RegistryHelperZk.handleException(e);
        }
    }

    static class WatchProcessEvent {
        String env;
        String project;
        String segment;
        String projectVersion;
        String segmentVersion;
        String segmentFullName;
        TreeCacheEvent.Type changeType;
    }

    interface WatchProcess {
        void interrupt(WatchProcessEvent event) throws InterruptedException;

        void onChange(WatchProcessEvent event);
    }
}
